package com.ilop.siteraudio.view;

import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.AsyncTask;
import android.os.Handler.Callback;
import android.os.Message;
import android.view.SurfaceView;

import java.util.ArrayList;
import java.util.Date;

/**
 * @author skygge
 * @date 1/27/21.
 * GitHub：javofxu@github.com
 * email：skygge@yeah.net
 * description：录音波形绘制
 */
public class WaveCanvas {
    
    private static final String TAG = WaveCanvas.class.getSimpleName();
    
    private final ArrayList<Short> inBuf = new ArrayList<>();//缓冲区数据
    private final ArrayList<byte[]> write_data = new ArrayList<>();//写入文件数据
    private boolean isRecording = false;// 录音线程控制标记
    private final int rateX = 100;//控制多少帧取一帧
    private final int marginRight = 30;//波形图绘制距离右边的距离
    private float divider = 0.2f;//为了节约绘画时间，每0.2个像素画一个数据
    private int mLineOff ;//上下边距的距离
    private long mCurrentTime;//当前时间戳

    private static final int FREQUENCY = 44100;// 设置音频采样率，44100是目前的标准，但是某些设备仍然支持22050，16000，11025
    private static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;// 设置单声道声道
    private static final int AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;// 音频数据格式：每个样本16位
    public final static int AUDIO_SOURCE = MediaRecorder.AudioSource.MIC;// 音频获取源
    private AudioRecord mAudioRecord;

    private Paint circlePaint;
    private Paint center;
    private Paint mPaint;

    /**
     * 开始录音
     */
    public void onStart(SurfaceView sfv, Callback callback) {
        init();
        isRecording = true;
        RecordTask mRecordTask = new RecordTask(sfv, mPaint, callback);
        mRecordTask.execute();
    }

    public void init(){
        circlePaint = new Paint();//画圆
        circlePaint.setColor(Color.rgb(246, 131, 126));//设置上圆的颜色

        center = new Paint();//中心线
        center.setColor(Color.rgb(76, 128, 248));// 画笔为color
        center.setStrokeWidth(1);// 设置画笔粗细
        center.setAntiAlias(true);
        center.setFilterBitmap(true);
        center.setStyle(Style.FILL);

        mPaint = new Paint();
        mPaint.setColor(Color.rgb(76, 128, 248));// 画笔为color
        mPaint.setStrokeWidth(1);// 设置画笔粗细
        mPaint.setAntiAlias(true);
        mPaint.setFilterBitmap(true);
        mPaint.setStyle(Style.FILL);
    }


    /**
     * 停止录音
     */
    public void onStop() {
        isRecording = false;
        inBuf.clear();// 清除
        mAudioRecord.stop();
        mAudioRecord.release();
    }

    /**
     * 清楚数据
     */
    public void clear(){
        inBuf.clear();// 清除
    }

    /**
     * 异步录音程序
     * @author cokus
     *
     */
    @SuppressLint("StaticFieldLeak")
    class RecordTask extends AsyncTask<Object, Object, Object> {
        private final int recBufSize;
        private final SurfaceView sfv;// 画板
        private final Paint mPaint;// 画笔
        private final Callback callback;


        public RecordTask(SurfaceView sfv, Paint mPaint, Callback callback) {
            recBufSize = AudioRecord.getMinBufferSize(FREQUENCY, CHANNEL_CONFIG, AUDIO_ENCODING);// 录音组件
            mAudioRecord = new AudioRecord(AUDIO_SOURCE,// 指定音频来源，这里为麦克风
                    FREQUENCY, // 48000HZ采样频率
                    CHANNEL_CONFIG,// 录制通道
                    AUDIO_SOURCE,// 录制编码格式
                    recBufSize);// 录制缓冲区大小 //先修改
            this.sfv = sfv;
            this.mPaint = mPaint;
            this.callback = callback;
            mLineOff = ((WaveSurfaceView)sfv).getLine_off();
            inBuf.clear();// 清除  换缓冲区的数据
        }

        @Override
        protected Object doInBackground(Object... params) {
            try {
                short[] buffer = new short[recBufSize];
                mAudioRecord.startRecording();// 开始录制
                while (isRecording) {
                    if (isCancelled()){
                        break;
                    }
                    // 从MIC保存数据到缓冲区
                    int readSize = mAudioRecord.read(buffer, 0, recBufSize);
                    synchronized (inBuf) {
                        for (int i = 0; i < readSize; i += rateX) {
                            inBuf.add(buffer[i]);
                        }
                    }
                    publishProgress();
                    if (AudioRecord.ERROR_INVALID_OPERATION != readSize) {
                        synchronized (write_data) {
                            byte[] bys = new byte[readSize *2];
                            //因为arm字节序问题，所以需要高低位交换
                            for (int i = 0; i < readSize; i++) {
                                byte[] ss =	getBytes(buffer[i]);
                                bys[i*2] =ss[0];
                                bys[i*2+1] = ss[1];
                            }
                            write_data.add(bys);
                        }
                    }
                }
            } catch (Throwable t) {
                Message msg = new Message();
                msg.arg1 =-2;
                msg.obj=t.getMessage();
                callback.handleMessage(msg);
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Object... values) {
            long time = new Date().getTime();
            if(time - mCurrentTime >= 5){ //两次绘图间隔的时间
                ArrayList<Short> buf;
                synchronized (inBuf) {
                    if (inBuf.size() == 0)
                        return;
                    while(inBuf.size() > (sfv.getWidth()-marginRight) / divider){
                        inBuf.remove(0);
                    }
                    buf = (ArrayList<Short>) inBuf.clone();// 保存
                }
                SimpleDraw(buf, sfv.getHeight()/2);// 把缓冲区数据画出来
                mCurrentTime = new Date().getTime();
            }
            super.onProgressUpdate(values);
        }

        public byte[] getBytes(short s) {
            byte[] buf = new byte[2];
            for (int i = 0; i < buf.length; i++) {
                buf[i] = (byte) (s & 0x00ff);
                s >>= 8;
            }
            return buf;
        }

        /**
         * 绘制指定区域
         *
         * @param buf
         *            缓冲区
         * @param baseLine
         *            Y轴基线
         */
        void SimpleDraw(ArrayList<Short> buf, int baseLine) {
            if (!isRecording) return;
            //波形图绘制距离左边的距离
            int marginLeft = 20;
            divider = (float) ((sfv.getWidth()-marginRight- marginLeft)/(48000/rateX*20.00));
            for (int i = 0; i < buf.size(); i++) {
                byte[] bus = getBytes(buf.get(i));
                buf.set(i, (short)((bus[1]) << 8 | bus[0]));//高低位交换
            }
            Canvas canvas = sfv.getHolder().lockCanvas(new Rect(0, 0, sfv.getWidth(), sfv.getHeight()));// 关键:获取画布
            if(canvas==null) return;
            canvas.drawARGB(255, 239, 239, 239);
            int start =(int) ((buf.size())* divider);
            float y;
            if(sfv.getWidth() - start <= marginRight){//如果超过预留的右边距距离
                start = sfv.getWidth() -marginRight;//画的位置x坐标
            }
            canvas.drawCircle(start, mLineOff/2f, mLineOff/10f, circlePaint);// 上圆
            canvas.drawCircle(start, sfv.getHeight()-mLineOff/2f-1, mLineOff/10f, circlePaint);// 下圆
            canvas.drawLine(start, mLineOff/2f, start, sfv.getHeight()-mLineOff/2f, circlePaint);//垂直的线
            int height = sfv.getHeight()-mLineOff;
            canvas.drawLine(0, height*0.5f+mLineOff/2f, sfv.getWidth() ,height*0.5f+mLineOff/2f, center);//中心线

            for (int i = 0; i < buf.size(); i++) {
                y =buf.get(i)/100f + baseLine;// 调节缩小比例，调节基准线
                float x=(i) * divider;
                if(sfv.getWidth() - (i-1) * divider <= marginRight){
                    x = sfv.getWidth()-marginRight;
                }
                //画线的方式很多，你可以根据自己要求去画。这里只是为了简单
                float y1 = sfv.getHeight() - y;
                if(y<mLineOff/2f){
                    y = mLineOff / 2f;
                }
                if(y>sfv.getHeight()-mLineOff/2f-1){
                    y = sfv.getHeight() - mLineOff / 2f - 1;
                }
                if(y1<mLineOff/2f){
                    y1 = mLineOff / 2f;
                }
                if(y1>(sfv.getHeight()-mLineOff/2f-1)){
                    y1 = (sfv.getHeight() - mLineOff / 2f - 1);
                }
                canvas.drawLine(x, y,  x,y1, mPaint);//中间出波形
            }
            sfv.getHolder().unlockCanvasAndPost(canvas);// 解锁画布，提交画好的图像
        }
    }
}   
